<?php

namespace app\bll;

use core\lib\schema\Table as SchemaTable;
use Doctrine\DBAL\Types\Types;
use Exception;
use Illuminate\Database\Schema\Blueprint;
use support\Db;
use Throwable;

class UserField
{


    /**
     * 获取单个字段
     *
     * @param integer $id
     * @return null|array
     */
    public function find(int $id): null|array
    {
        $data =  Db::table('frame_user_field')
            ->where('id', '=', $id)
            ->first();
        return $data === null ? [] : get_object_vars($data);
    }

    /**
     * 查询字段
     *
     * @return array
     */
    public function select(string $tablename): array
    {
        return Db::table('frame_user_field')
            ->where('tablename', '=', $tablename)
            ->orderBy('sort')
            ->get()
            ->map(function ($value) {
                return (array)$value;
            })
            ->toArray();
    }

    /**
     * 某表字段最大排序值
     *
     * @param string $tablename
     * @return integer
     */
    public function maxSort(string $tablename): int
    {
        $maxsort = Db::table('frame_user_field')
            ->where('tablename', '=', $tablename)
            ->max('sort');
        return $maxsort ? $maxsort : 0;
    }

    /**
     * 判断字段是否存在
     *
     * @param string $fieldname
     * @return boolean
     */
    public function fieldExists(string $fieldname): bool
    {
        return Db::table('frame_user_field')
            ->where('name', '=', $fieldname)
            ->exists();
    }


    public function someFieldExists(array $fieldnames)
    {
        return  Db::table('frame_user_field')
            ->whereIn('name', $fieldnames)
            ->exists();
    }

    private function addColumn(
        Blueprint $table,
        string $type,
        string $name,
        mixed $default = ''
    ) {
        switch ($type) {
            case 'string':
                $table->string($name)->default($default == '' ? null : $default)->nullable();
                break;
            case 'decimal':
                $table->decimal($name)->default($default == '' ? null : $default)->nullable();
                break;
            case 'text':
                $table->text($name)->default($default == '' ? null : $default)->nullable();
                break;
            case 'integer':
                $table->integer($name)->default($default == '' ? null : $default)->nullable();
                break;
            case 'date':
                $table->date($name)->default($default == '' ? null : $default)->nullable();
                break;
            case 'datetime':
                $table->dateTime($name)->default($default == '' ? null : $default)->nullable();
                break;
        }
    }

    private function editColumn(
        Blueprint $table,
        string $type,
        string $name,
        mixed $default = ''
    ) {
        switch ($type) {
            case 'string':
                $table->string($name)->default($default == '' ? null : $default)->change();
                break;
            case 'decimal':
                $table->decimal($name)->default($default == '' ? null : $default)->change();
                break;
            case 'text':
                $table->text($name)->default($default == '' ? null : $default)->change();
                break;
            case 'integer':
                $table->integer($name)->default($default == '' ? null : $default)->change();
                break;
            case 'date':
                $table->date($name)->default($default == '' ? null : $default)->nullable();
                break;
            case 'datetime':
                $table->dateTime($name)->default($default == '' ? null : $default)->change();
                break;
        }
    }

    /**
     * 获取注册字段
     * 
     *
     * @return array
     */
    public function selRegisterField(): array
    {
        return Db::table('frame_user_field')
            ->where('register', '=', 1)
            ->where('tablename', '=', 'frame_user')
            ->orderBy('sort')
            ->get()
            ->map(function ($value) {
                return (array)$value;
            })
            ->toArray();
    }


    /**
     * 批量排序
     *
     * @param array|string $ids
     * @throws Exception
     * @return void
     */
    public function batchSort(array|string $ids)
    {

        try {
            if (is_string($ids)) {
                $ids = explode(',', $ids);
            }

            foreach ($ids as $key => $id) {
                Db::table('frame_user_field')
                    ->where('id', '=', $id)
                    ->update([
                        'sort' => $key
                    ]);
            }
        } catch (Exception $ex) {
            throw $ex;
        }
    }



    public function batchAdd(array $fieldData, string $tablename)
    {
        try {

            $schemaTable = new SchemaTable($tablename);
            $columns     = $schemaTable->getColumns();
            $columns     = array_column($columns, 'name');

            foreach ($fieldData as $key => $field) {
                //验证表中字段是否存在
                if (in_array($field['name'], $columns)) {
                    throw new Exception('字段已存在【' . $field['name'] . '】');
                }
            }

            $maxsort = $this->maxSort($tablename);
            foreach ($fieldData as $key => $field) {
                $maxsort++;
                $field['sort']      = $maxsort;
                $field['tablename'] = $tablename;
                Db::table('frame_user_field')
                    ->insert($field);
                //循环添加字段
                Db::schema()->table($tablename, function (Blueprint $table) use ($field) {
                    $this->addColumn($table, $field['type'], $field['name'], $field['default']);
                });
            }
        } catch (Exception $ex) {

            throw  $ex;
        }
    }

    public function batchEdit(array $newfields, string $tablename)
    {

        $schemaTable = new SchemaTable($tablename);
        $columns     = $schemaTable->getColumns(); //真实表里的字段
        $columns     = array_column($columns, 'name');

        $modelfields     = $this->select($tablename);  //model表里的字段
        $modelexitfields = [];

        foreach ($modelfields as $field) {
            $modelexitfields[$field['name']] = $field['id'];
        }

        $diffcloumns = array_diff($columns, array_keys($modelexitfields));
        $diffcloumns = array_values($diffcloumns);

        foreach ($newfields as $key => $field) {
            if (in_array($field['name'],  $diffcloumns)) {
                throw new Exception($field['name'] . '字段已存在');
            }

            if (
                isset($modelexitfields[$field['name']])
                && $modelexitfields[$field['name']] != $key
            ) {
                throw new Exception($field['name'] . '字段已存在');
            }
        }

        try {
            foreach ($newfields as $key => $newfield) {
                $oldfield = $this->find($key);
                if ($newfield['name'] != $oldfield['name']) {
                    Db::schema()->table($tablename, function (Blueprint $table) use ($oldfield, $newfield) {
                        //修改字段名
                        $table->renameColumn($oldfield['name'], $newfield['name']);
                    });
                }

                if (
                    $newfield['type'] != $oldfield['type']
                    || $newfield['default'] != $oldfield['default']
                ) {
                    Db::schema()->table($tablename, function (Blueprint $table) use ($oldfield, $newfield) {

                        //修改字段类型
                        $this->editColumn($table, $newfield['type'], $newfield['name'], $newfield['default']);
                    });
                }

                Db::table('frame_user_field')
                    ->where('id', '=', $key)
                    ->update($newfield);
            }
        } catch (Exception $ex) {
            throw $ex;
        }
    }


    /**
     * 批量删除
     *
     * @param array $ids
     * @throws Exception
     * @return void
     */
    public function batchDelete(array $ids)
    {
        foreach ($ids as $id) {

            try {
                $field = $this->find($id);
                if ($field['issystem'] == 1) {
                    continue;
                }
                Db::schema()->table($field['tablename'], function (Blueprint $table) use ($field) {
                    $table->dropColumn($field['name']);
                });

                Db::table('frame_user_field')
                    ->where('id', '=', $id)
                    ->delete();
            } catch (Exception $ex) {
                throw  $ex;
            }
        }
    }


    /**
     * 保存字段 自动判断删除 修改还是新增
     *
     * @param array $data
     * @param string $tablename 表名
     * @throws Exception
     * @return void
     */
    public function batchsave(array $data, string $tablename)
    {

        try {

            $tempfieldname = [];
            //先检查字段名有无重复
            foreach ($data as $d) {
                if (in_array(trim($d['name']), $tempfieldname)) {
                    throw new Exception($d['name'] . '字段重复');
                }
                $tempfieldname[] = trim($d['name']);
            }

            $addarr = [];
            $delarr = [];
            $editarr = [];
            foreach ($data as $key => $d) {
                $d['reg'] = htmlspecialchars_decode($d['reg']); //reg转码

                if (!isset($d['register'])) {
                    $d['register'] = 0;
                }
                if (!isset($d['unique'])) {
                    $d['unique'] = 0;
                }
                if (!isset($d['require'])) {
                    $d['require'] = 0;
                }

                if (!isset($d['editor'])) {
                    $d['editor'] = 0;
                }

                $d['sort'] = $key;
                switch ($d['op']) {
                    case 'add':
                        unset($d['op']);
                        unset($d['id']);
                        $addarr[] = $d;
                        break;
                    case 'delete':
                        $delarr[] = $d['id'];
                        break;
                    case 'edit':
                        $id = $d['id'];
                        unset($d['op']);
                        unset($d['id']);
                        $editarr[$id] = $d;

                        break;
                }
            }

            if ($delarr) {
                $this->batchDelete($delarr);
            }

            if ($addarr) {
                $this->batchAdd($addarr, $tablename);
            }

            if ($editarr) {

                $this->batchEdit($editarr, $tablename);
            }
        } catch (Exception $ex) {
            throw $ex;
        }
    }
}
